package com.qiangesoft.easyexcel.read;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;

/**
 * excel读取监听类
 *
 * @author qiangesoft
 * @date 2024-04-10
 */
@Slf4j
public class ReadDataListener implements ReadListener<ReadData> {

    /**
     * 每隔100条存储数据库，然后清理list
     */
    private static final int BATCH_COUNT = 100;

    /**
     * 缓存的数据
     */
    private List<ReadData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    /**
     * 构造方法
     */
    public ReadDataListener() {
    }

    /**
     * 异常处理：默认为抛出异常
     *
     * @param exception
     * @param context
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            log.error("第{}行，第{}列解析异常，数据为:{}", excelDataConvertException.getRowIndex(), excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
        }
    }

    /**
     * 解析头部
     *
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        log.info("{}条头部解析完成！", headMap.size());
    }

    /**
     * 每一条数据解析都会调用
     *
     * @param data
     * @param context
     */
    @Override
    public void invoke(ReadData data, AnalysisContext context) {
        cachedDataList.add(data);
        // 达到BATCH_COUNT了，需要去存储一次数据库，防止几万条数据在内存，造成OOM
        if (cachedDataList.size() >= BATCH_COUNT) {

            this.saveData();

            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 确保最后遗留的数据也存储到数据库
        this.saveData();

        log.info("所有数据解析完成！");
    }

    /**
     * 存储数据
     */
    private void saveData() {
        log.info("{}条数据存储数据库成功！", cachedDataList.size());
    }
}
